﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESpace;
using System.Windows.Forms;
using AMVInfoSpace;

namespace AMVCommander
{
    public class Music : IComparable<Music>
    {
        public string Author;
        public string Name;
        public Music(string Author, string Name) { this.Author = Author; this.Name = Name; }

        public Music Clone()
        {
            return new Music(Author, Name);
        }

        public int CompareTo(Music m)
        {
            int ret = Name.ToUpper().CompareTo(m.Name.ToUpper());
            if (ret == 0) return Author.ToUpper().CompareTo(m.Author.ToUpper());
            else return ret;
        }

        public override string ToString() { return Author + " -> " + Name; }

    }

    public class AMVSite
    {
        public int ID;
        public decimal Rating;
        public DateTime LastUpdateRating;

        /*public AMVSite()
        {
            this.ID = -1;
            this.Rating = 0;
            this.LastUpdateRating = DateTime.MinValue;
        }*/

        public AMVSite(int ID, decimal Rating, DateTime LastUpdateRating)
        {
            this.ID = ID;
            this.Rating = Rating;
            this.LastUpdateRating = LastUpdateRating;
        }

        public AMVSite Clone()
        {
            return new AMVSite(ID, Rating, LastUpdateRating);
        }
    }

    public class AMV : IComparable<AMV>
    {
        int ActVer = 5;

        public String Name;
        public String FLocalPath;

        public String FullPath { get { return Gl.Sett.DPathHomeDir + FLocalPath; } }

        public string MD5Len = ""; // он же уникальный идентификатор AMV двух в базе быть не должно
        public long Len;

        public String Comment = "";

        /// <summary>список тэгов</summary>
        public List<string> Tags = new List<string>();
        /// <summary>список ключевых слов</summary>
        public List<string> KeyWords = new List<string>();
        /// <summary>список авторов</summary>
        public List<String> Atr = new List<string>();
        /// <summary>список музыки</summary>
        public List<Music> Msc = new List<Music>();
        /// <summary>список аниме</summary>
        public List<String> Anm = new List<string>();
        /// <summary>список наград</summary>
        public List<String> Awd = new List<String>();

        public enum Status { Normal, NotEqual, NotExist };
        public Status Stat = Status.Normal;

        // Данные для отображения
        public ListViewItem itm = new ListViewItem("");

        public Dictionary<SITE, AMVSite> Sites = new Dictionary<SITE, AMVSite>();

        public int AMVNewsNum { get { if (!Sites.ContainsKey(SITE.AMVNews))return -1; return Sites[SITE.AMVNews].ID; } }
        public int AkrossNum { get { if (!Sites.ContainsKey(SITE.Akross)) return -1; return Sites[SITE.Akross].ID; } }
        public int CreaConNum { get { if (!Sites.ContainsKey(SITE.CreaCon)) return -1; return Sites[SITE.CreaCon].ID; } }

        public decimal MiddleRating
        {
            get
            {
                if (Sites.Count == 0) return 0;
                decimal sumRating = 0;
                int cnt = 0;
                foreach (KeyValuePair<SITE, AMVSite> site in Sites)
                {
                    if (site.Value.Rating != 0)
                    {
                        sumRating += site.Value.Rating;
                        cnt++;
                    }
                }
                if (cnt == 0) return 0;
                return sumRating / (decimal)cnt;
            }
        }

        /// <summary>Файл скачен с сайта AMVNews, не привязан, и его можно привязать к сайту</summary>
        public bool IsPossableAMVNewsLink { get { return !Sites.ContainsKey(SITE.AMVNews) && GetAMVNewsIdByFileName() != -1; } }

        public AMV(string Name, string FPath, string MD5Len, long Len, Status Stat)
        {
            this.Name = Name;
            this.FLocalPath = FPath;
            this.MD5Len = MD5Len;
            this.Len = Len;
            this.Stat = Stat;
        }

        public AMV(EStream st)
        {
            int ver = st.LoadInt();
            switch (ver)
            {
                case 0: Load0(st); break;
                case 1: Load1(st); break;
                case 2: Load2(st); break;
                case 3: Load3(st); break;
                case 4: Load4(st); break;
                case 5: Load5(st); break;
                default: throw new Exception("Неизвестная версия AMV");
            }
        }

        public void UpdateItm(ListView.ColumnHeaderCollection Columns)
        {
            // если количество столбцов поменялось
            while (Columns.Count < itm.SubItems.Count) itm.SubItems.RemoveAt(itm.SubItems.Count - 1);
            while (Columns.Count > itm.SubItems.Count) itm.SubItems.Add("");

            itm.SubItems[0].Text = Name;
            for (int g = 1; g < Columns.Count; g++)
            {
                Column clm = (Column)Columns[g].Tag;

                switch (clm.Tp)
                {
                    case ColumnType.FPath: { itm.SubItems[g].Text = FLocalPath; break; }
                    case ColumnType.Len: { itm.SubItems[g].Text = Gl.GetLenString(Len); break; }
                    case ColumnType.Rating: { itm.SubItems[g].Text = MiddleRating == 0 ? "" : Math.Round(MiddleRating, 2).ToString("F2"); break; }
                    case ColumnType.Author:
                        {
                            string str = "";
                            for (int j = 0; j < Atr.Count; j++)
                            {
                                if (j > 0) str += " || ";
                                str += Atr[j];
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }
                    case ColumnType.Music:
                        {
                            string str = "";
                            for (int j = 0; j < Msc.Count; j++)
                            {
                                if (j > 0) str += " || ";
                                str += Msc[j].Author + " - " + Msc[j].Name;
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }

                    case ColumnType.Anime:
                        {
                            string str = "";
                            for (int j = 0; j < Anm.Count; j++)
                            {
                                if (j > 0) str += " || ";
                                str += Anm[j];
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }

                    case ColumnType.Tag:
                        {
                            string str = "";
                            for (int j = 0; j < Tags.Count; j++)
                            {
                                if (j > 0) str += " || ";
                                str += Tags[j];
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }
                    case ColumnType.KeyWords:
                        {
                            string str = "";
                            for (int j = 0; j < KeyWords.Count; j++)
                            {
                                if (j > 0) str += " || ";
                                str += KeyWords[j];
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }

                    case ColumnType.Awards:
                        {
                            string str = "";
                            for (int j = 0; j < Awd.Count; j++)
                            {
                                if (j > 0) str += ", ";
                                str += Awd[j];
                            }
                            itm.SubItems[g].Text = str;
                            break;
                        }
                }
            }

        }

        #region *** Save/Load *******************************************************

        public void Save(EStream st)
        {
            st.SaveInt(ActVer);

            st.SaveStr(Name);
            st.SaveStr(FLocalPath);
            st.SaveStr(Comment);
            st.SaveStr(MD5Len);
            st.SaveLong(Len);
            st.SaveInt((int)Stat);
            st.SaveBool(false); //бывшее AutoAdd

            // теги
            st.SaveInt(Tags.Count);
            for (int i = 0; i < Tags.Count; i++) st.SaveStr(Tags[i]);

            // ключевые слова
            st.SaveInt(KeyWords.Count);
            for (int i = 0; i < KeyWords.Count; i++) st.SaveStr(KeyWords[i]);

            // авторы
            st.SaveInt(Atr.Count);
            for (int i = 0; i < Atr.Count; i++) st.SaveStr(Atr[i]);

            // аниме
            st.SaveInt(Anm.Count);
            for (int i = 0; i < Anm.Count; i++) st.SaveStr(Anm[i]);

            // музыка
            st.SaveInt(Msc.Count);
            for (int i = 0; i < Msc.Count; i++) { st.SaveStr(Msc[i].Author); st.SaveStr(Msc[i].Name); }

            // награды
            st.SaveInt(Awd.Count);
            for (int i = 0; i < Awd.Count; i++) { st.SaveStr(Awd[i]); }

            // привязка к сайтам
            st.SaveInt(Sites.Count);
            foreach (KeyValuePair<SITE, AMVSite> site in Sites)
            {
                st.SaveByte((byte)site.Key);
                st.SaveInt(site.Value.ID);
                st.SaveInt((int)(site.Value.Rating * 10000));
                st.SaveDate(site.Value.LastUpdateRating);
            }
        }

        void Load0(EStream st)
        {
            Name = st.LoadStr();
            FLocalPath = st.LoadStr();
            Comment = st.LoadStr();
            MD5Len = st.LoadStr();
            Len = st.LoadLong();
            Stat = (Status)st.LoadInt();
            st.LoadBool(); // бывшее AutoAdd

            // теги
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Tags.Add(st.LoadStr());

            // авторы
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Atr.Add(st.LoadStr());

            // аниме
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Anm.Add(st.LoadStr());

            // музыка
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Msc.Add(new Music(st.LoadStr(), st.LoadStr()));

            // награды
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Awd.Add(st.LoadStr() + " >> " + st.LoadStr());
        }

        void Load1(EStream st)
        {
            Name = st.LoadStr();
            FLocalPath = st.LoadStr();
            Comment = st.LoadStr();
            MD5Len = st.LoadStr();
            Len = st.LoadLong();
            Stat = (Status)st.LoadInt();
            st.LoadBool(); // бывшее AutoAdd

            // теги
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Tags.Add(st.LoadStr());

            // ключевые слова
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) KeyWords.Add(st.LoadStr());

            // авторы
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Atr.Add(st.LoadStr());

            // аниме
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Anm.Add(st.LoadStr());

            // музыка
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Msc.Add(new Music(st.LoadStr(), st.LoadStr()));

            // награды
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Awd.Add(st.LoadStr() + " >> " + st.LoadStr());
        }

        void Load2(EStream st)
        {
            Name = st.LoadStr();
            FLocalPath = st.LoadStr();
            Comment = st.LoadStr();
            MD5Len = st.LoadStr();
            Len = st.LoadLong();
            Stat = (Status)st.LoadInt();
            st.LoadBool(); // бывшее AutoAdd

            // теги
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Tags.Add(st.LoadStr());

            // ключевые слова
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) KeyWords.Add(st.LoadStr());

            // авторы
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Atr.Add(st.LoadStr());

            // аниме
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Anm.Add(st.LoadStr());

            // музыка
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Msc.Add(new Music(st.LoadStr(), st.LoadStr()));

            // награды
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Awd.Add(st.LoadStr());

            // привязка к сайтам
            int AMVNewsNum = st.LoadInt();
            int AkrossNum = st.LoadInt();
            if (AMVNewsNum != -1) Sites.Add(SITE.AMVNews, new AMVSite(AMVNewsNum, 0, DateTime.MinValue));
            if (AkrossNum != -1) Sites.Add(SITE.Akross, new AMVSite(AkrossNum, 0, DateTime.MinValue));
        }

        void Load3(EStream st)
        {
            Name = st.LoadStr();
            FLocalPath = st.LoadStr();
            Comment = st.LoadStr();
            MD5Len = st.LoadStr();
            Len = st.LoadLong();
            Stat = (Status)st.LoadInt();
            st.LoadBool(); // бывшее AutoAdd

            // теги
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Tags.Add(st.LoadStr());

            // ключевые слова
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) KeyWords.Add(st.LoadStr());

            // авторы
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Atr.Add(st.LoadStr());

            // аниме
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Anm.Add(st.LoadStr());

            // музыка
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Msc.Add(new Music(st.LoadStr(), st.LoadStr()));

            // награды
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Awd.Add(st.LoadStr());

            // привязка к сайтам
            int AMVNewsNum = st.LoadInt();
            int AkrossNum = st.LoadInt();
            int CreaConNum = st.LoadInt();
            if (AMVNewsNum != -1) Sites.Add(SITE.AMVNews, new AMVSite(AMVNewsNum, 0, DateTime.MinValue));
            if (AkrossNum != -1) Sites.Add(SITE.Akross, new AMVSite(AkrossNum, 0, DateTime.MinValue));
            if (CreaConNum != -1) Sites.Add(SITE.CreaCon, new AMVSite(CreaConNum, 0, DateTime.MinValue));
        }

        void Load4(EStream st)
        {
            Load3(st);
            // рейтинг
            DateTime RatingLasUpdate = st.LoadDate();
            decimal Rating = st.LoadInt() / (decimal)10000.0;

            if (Sites.ContainsKey(SITE.AMVNews)) { Sites[SITE.AMVNews].Rating = Rating; Sites[SITE.AMVNews].LastUpdateRating = RatingLasUpdate; }
            else if (Sites.ContainsKey(SITE.Akross)) { Sites[SITE.Akross].Rating = Rating; Sites[SITE.Akross].LastUpdateRating = RatingLasUpdate; }
            else if (Sites.ContainsKey(SITE.CreaCon)) { Sites[SITE.CreaCon].Rating = Rating; Sites[SITE.CreaCon].LastUpdateRating = RatingLasUpdate; }
        }

        void Load5(EStream st)
        {
            Name = st.LoadStr();
            FLocalPath = st.LoadStr();
            Comment = st.LoadStr();
            MD5Len = st.LoadStr();
            Len = st.LoadLong();
            Stat = (Status)st.LoadInt();
            st.LoadBool(); // бывшее AutoAdd

            // теги
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Tags.Add(st.LoadStr());

            // ключевые слова
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) KeyWords.Add(st.LoadStr());

            // авторы
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Atr.Add(st.LoadStr());

            // аниме
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Anm.Add(st.LoadStr());

            // музыка
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Msc.Add(new Music(st.LoadStr(), st.LoadStr()));

            // награды
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++) Awd.Add(st.LoadStr());

            // привязка к сайтам
            kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++)
                Sites.Add((SITE)st.LoadByte(), new AMVSite(st.LoadInt(), st.LoadInt() / (decimal)10000.0, st.LoadDate()));

        }

        #endregion *** Save/Load *******************************************************

        /// <summary>Список привязок только к тем сайтам, у которых есть рейтинг</summary>
        public Dictionary<SITE, AMVSite> GetSitesWithRating()
        {
            Dictionary<SITE, AMVSite> ret = new Dictionary<SITE, AMVSite>();
            foreach (KeyValuePair<SITE, AMVSite> site in Sites) if (site.Value.Rating != 0) ret.Add(site.Key, site.Value);
            return ret;
        }

        public AMV Clone()
        {
            AMV ret = new AMV(Name, FLocalPath, MD5Len, Len, Stat);

            ret.Comment = Comment;

            for (int i = 0; i < Atr.Count; i++)
                ret.Atr.Add(Atr[i]);
            for (int i = 0; i < Msc.Count; i++)
                ret.Msc.Add(Msc[i].Clone());
            for (int i = 0; i < Anm.Count; i++)
                ret.Anm.Add(Anm[i]);
            for (int i = 0; i < Awd.Count; i++)
                ret.Awd.Add(Awd[i]);
            for (int i = 0; i < Tags.Count; i++)
                ret.Tags.Add(Tags[i]);
            for (int i = 0; i < KeyWords.Count; i++)
                ret.KeyWords.Add(KeyWords[i]);

            ret.Sites.Clear();
            foreach (KeyValuePair<SITE, AMVSite> site in Sites)
                ret.Sites.Add(site.Key, site.Value.Clone());

            return ret;
        }

        public void CopyFrom(AMV amv)
        {
            Name = amv.Name;
            FLocalPath = amv.FLocalPath;

            MD5Len = amv.MD5Len;
            Len = amv.Len;
            Comment = amv.Comment;

            Atr.Clear();
            Msc.Clear();
            Anm.Clear();
            Awd.Clear();
            Tags.Clear();
            KeyWords.Clear();
            for (int i = 0; i < amv.Atr.Count; i++)
                Atr.Add(amv.Atr[i]);
            for (int i = 0; i < amv.Msc.Count; i++)
                Msc.Add(amv.Msc[i].Clone());
            for (int i = 0; i < amv.Anm.Count; i++)
                Anm.Add(amv.Anm[i]);
            for (int i = 0; i < amv.Awd.Count; i++)
                Awd.Add(amv.Awd[i]);
            for (int i = 0; i < amv.Tags.Count; i++)
                Tags.Add(amv.Tags[i]);
            for (int i = 0; i < amv.KeyWords.Count; i++)
                KeyWords.Add(amv.KeyWords[i]);

            Sites.Clear();
            foreach (KeyValuePair<SITE, AMVSite> site in amv.Sites)
                Sites.Add(site.Key, site.Value.Clone());

        }

        public enum CompareMode { Name, FPath, Len, Tag, Author, Music, Anime, Awards, Rating, KeyWords }
        public static CompareMode CmprMode = CompareMode.Name; // индекс сортировки
        public static bool CmprUp = true; // сортировать по возрастанию
        public int CompareTo(AMV amv)
        {
            int ret = 0;
            switch (CmprMode)
            {
                case CompareMode.Name: return CmprUp ? Name.ToUpper().CompareTo(amv.Name.ToUpper()) : Name.CompareTo(amv.Name) * -1;
                case CompareMode.FPath: { ret = FLocalPath.ToUpper().CompareTo(amv.FLocalPath.ToUpper()); break; }
                case CompareMode.Tag: { ret = Gl.CmprStrL(Tags, amv.Tags); break; }
                case CompareMode.Author: { ret = Gl.CmprStrL(Atr, amv.Atr); break; }
                case CompareMode.Music: { ret = Gl.CmprMusicL(Msc, amv.Msc); break; }
                case CompareMode.Anime: { ret = Gl.CmprStrL(Anm, amv.Anm); break; }
                case CompareMode.Awards: { ret = Gl.CmprStrL(Awd, amv.Awd); break; }
                case CompareMode.KeyWords: { ret = Gl.CmprStrL(KeyWords, amv.KeyWords); break; }
                case CompareMode.Len: { ret = Len.CompareTo(amv.Len); break; }
                case CompareMode.Rating: { ret = MiddleRating.CompareTo(amv.MiddleRating); break; }
            }
            if (ret == 0) return Name.ToUpper().CompareTo(amv.Name.ToUpper());
            return CmprUp ? ret : ret * -1;
        }

        /// <summary>Получение идентификатора для AMVNews, если он присутствует в названии файла. Возвращает -1, если название файла НЕ содержит ID</summary>
        public int GetAMVNewsIdByFileName()
        {
            string fName = new FileInfo(FullPath).Name;
            if (!fName.Contains(".amvnews.ru")) return -1;
            string[] ssl = fName.Split('.');
            if (ssl.Length < 2) return -1;
            if (ssl[0].Length < 5) return -1;

            int num = 0;
            if (!int.TryParse(ssl[0], out num)) return -1;
            return num;
        }

    }
}
